/*
 * Copyright 2007 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */


package bw.news;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import bw.util.*;
import bw.os.*;

import javax.xml.parsers.*;

public class NewsManager
{
    public static final String NEWS_MANAGER_KEY = "NEWS_MANAGER";

    private static Object _lock = new Object();
    private static NewsManager _instance = null;

    private File _xmlNewsFile = null;
    private Vector _defaultCategories = null;

    public static NewsManager getInstance ()
    {
        if ( _instance == null ) 
        {
            synchronized ( _lock ) 
            {
                if ( _instance == null ) 
                {
                    _instance = new NewsManager();
                }
            }
        }

        return _instance;
    }

    private NewsManager ()
    {
        //nothing
    }

    public void init ( File xmlNewsFile )
    {
        _xmlNewsFile = xmlNewsFile;
    }

    public void syncChanges ( Vector cats )
    {
        try
        {
            //ObjectStore.getInstance().sync( NEWS_MANAGER_KEY );
            ObjectStore.getInstance().put( NEWS_MANAGER_KEY, cats );
        }
        catch ( Exception ex )
        {
            Log.getInstance().write( "Problem syncing changes in NewsManager!", ex );
        }
    }

    public Vector newsCategories ()
    {
        //attempt to get the news categories out of the object store. 
        Vector nc = null;
        try
        {
            nc = (Vector) ObjectStore.getInstance().get( NEWS_MANAGER_KEY );
        }
        catch ( Exception ex )
        {
            Log.getInstance().write( "Problem getting news categories!", ex );
            nc = new Vector();
        }

        //if didn't find any then parse the xml file
        /*if ( nc == null )
        {
            nc = getDefaultNewsCategories();

            //save the defaults in the object store. 
            try
            {
                ObjectStore.getInstance().put( NEWS_MANAGER_KEY, nc );
            }
            catch ( Exception ex )
            {
                Log.getInstance().write( "Problem saving news categories", ex );
            }
        }*/

        if ( nc == null ) { nc = new Vector(); }

        return nc;
    }

    private Vector getDefaultNewsCategories ()
    {
        if ( _defaultCategories == null )
        {
            _defaultCategories = parseXml( _xmlNewsFile );
        }

        return _defaultCategories;
    }

    public NewsSource getNewsSourceForId ( String id )
    {
        NewsSource source = null;

        if ( id != null ) 
        {
            Iterator sources = getNewsSources();
            while ( sources.hasNext() )
            {
                NewsSource s = (NewsSource) sources.next();
                if ( id.equals( s.getId() ) )
                {
                    source = s;
                    break;
                }
            }
        }

        return source;
    }

    public NewsSource newNewsSource ( String name, String src, String site, int refreshInt )
    {
        int id = 0; 
        for ( Enumeration e = newsCategories().elements(); e.hasMoreElements(); )
        {
            NewsCategory cat = (NewsCategory) e.nextElement();
            Vector sources = cat.getNewsSources();
            for ( Enumeration f = sources.elements(); f.hasMoreElements(); )
            {
                NewsSource source = (NewsSource) f.nextElement();
                id = ( ( source.getPrimaryId() >= id ) ? source.getPrimaryId() + 1 : id );
            }
        }

        NewsSource source = new NewsSource( id, name, src, site, refreshInt );

        return source;
    }

    public NewsCategory newNewsCategory ( String name )
    {
        Vector newsCategories = newsCategories();

        int id = 0;
        for ( Enumeration e = newsCategories.elements(); e.hasMoreElements(); )
        {
            NewsCategory cat = (NewsCategory) e.nextElement();
            id = ( ( cat.getId() >= id ) ? cat.getId() + 1 : id );
        }

        NewsCategory cat = new NewsCategory( id, name );
        newsCategories.addElement( cat );

        syncChanges( newsCategories );

        return cat;
    }

    public NewsCategory getNewsCategoryForId ( int id )
    {
        return getNewsCategoryForId( newsCategories(), id );
    }

    public NewsCategory getNewsCategoryForId ( Vector cats, int id )
    {
        NewsCategory category = null;
        
        //don't check for reparse because that might mess up the ids.
        Iterator categories = cats.iterator();
        while ( categories.hasNext() )
        {
            NewsCategory cat = (NewsCategory) categories.next();
            if ( id == cat.getId() )
            {
                category = cat;
                break;
            }
        }

        return category;
    }

    public Iterator getNewsSources ()
    {
        Vector sources = new Vector();
        Iterator cats = getNewsCategories();
        while ( cats.hasNext() )
        {
            NewsCategory cat = (NewsCategory) cats.next();
            sources.addAll( cat.getNewsSources() );
        }

        return sources.iterator();
    }

    public Iterator getNewsCategories ()
    {
        return newsCategories().iterator();
    }

    public void moveNewsSourceToCategory ( Vector cats, NewsSource source, NewsCategory cat, 
                                           int newCategoryId )
    {
        //this merely removes the source from this categories list
        cat.removeNewsSource( source );

        //add it to the list of the new NewsCategory
        NewsCategory nc = getNewsCategoryForId( cats, newCategoryId );
	    if ( nc != null ) {
            nc.attachNewsSource( source );
        }

  	    syncChanges( cats );	      
    }

    public void moveCategory ( NewsCategory cat, int pos )
    {
        if ( cat != null )
        {
            Vector newsCategories = newsCategories();
            newsCategories.removeElement( cat );
            newsCategories.insertElementAt( cat, pos );
            syncChanges( newsCategories );
        }
    }

    public void deleteCategory ( NewsCategory cat )
    {
        if ( cat != null )
        {
            cat.clearNewsSources();
            Vector cats = newsCategories();
            cats.removeElement( cat );
            syncChanges( cats );
        }
    }

    //public void handleObjectStoreNodeUpdateEvent ( ObjectStoreEvent event )
    //{
    //    Log.getInstance().write( "Updating NewsManager object store object!" );
    //    _newsCategories = (Vector) ObjectStore.getInstance().get( NEWS_MANAGER_KEY );
    //}

    public Vector parseXml ( File xmlFile )
    {
        SAXParser parser = null;

        try {
            parser = SAXParserFactory.newInstance().newSAXParser();
        }
        catch ( Exception ex ) { }

        Vector categories = new Vector();

        if ( parser != null ) 
        {
            NewsXMLHandler handler = new NewsXMLHandler();

            try 
            {
                parser.parse( xmlFile, handler );
            }
            catch ( Exception ex ) 
            { 
                Log.getInstance().write( "Problem parsing news xml file!", 
                                         ex );
            }
            
            categories = handler.getNewsSourcesByCategory();
        }

        return categories;
    }

    public static void writeNewsSources ( Vector categories, File xmlFile )
    {
        Log.getInstance().write( "Writing news sources!" );

        try 
        {
            PrintWriter out = new PrintWriter( new FileWriter( xmlFile ) );
            out.println( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );
            out.println( "<opml version=\"1.0\">" );
            out.println( "<head><title>BW News</title></head>" );
            out.println( "<body>" );
            Iterator cats = categories.iterator();
            while ( cats.hasNext() )
            {
                NewsCategory cat = (NewsCategory) cats.next();
                out.println( "<outline title=\"" + cat.getName() + "\" text=\"" +
                             cat.getName() + "\">" );
                Iterator sources = cat.getNewsSources().iterator();
                while ( sources.hasNext() ) 
                {
                    NewsSource source = (NewsSource) sources.next();
                    out.print( "<outline text=\"" );
                    out.print( Util.XMLEscape( source.getName() ) );
                    out.print( "\" title=\"" );
                    out.print( Util.XMLEscape( source.getName() ) );
                    out.print( "\" type=\"rss\" " );
                    out.print( "xmlUrl=\"" );
                    out.print( Util.XMLEscape( source.getSrc() ) );
                    out.print( "\"" );
                    if ( source.getSite() != null && source.getSite().length() > 0 )
                    {
                        out.print( " htmlUrl=\"" );
                        out.print( Util.XMLEscape( source.getSite() ) );
                        out.print( "\"" );
                    }
                    /*if ( source.getUsername() != null )
                    {
                        out.print( "\tusername=\"" );
                        out.print( Util.XMLEscape( source.getUsername() ) );
                        out.println( "\"" );
                    }
                    if ( source.getPassword() != null )
                    {
                        out.print( "\tpassword=\"" );
                        out.print( Util.XMLEscape( source.getPassword() ) );
                        out.println( "\"" );
                    }
                    out.print( "\trefresh=\"" );
                    out.print( Util.XMLEscape( source.getRefreshStr() ) );
                    */
                    out.println( " />" );
                }
                out.println( "</outline>" );
            }
            out.println( "</body>" );
            out.flush();
            out.close();
        }
        catch ( Exception ex )
        {
            Log.getInstance().write( "Problem writing news.xml file!", ex );
        }
    }

    public static void main ( String [] args )
    {
        if ( args.length != 2 )
        {
            System.out.println( "NewsManager <xml file> <http root>" );
            System.exit( 0 );
        }

        try
        {
            File xmlFile = new File( args[0] );

            /* -- Stuff for file based object store. doesn't work anymore.
            //this filename will always be the same.
            FileInputStream fis = new FileInputStream( args[1] + 
                                                       "/os-f51f244f0fc4cce1de04d2af2644a18c.obj" );
            GZIPInputStream gzin = new GZIPInputStream( fis );
            ObjectInputStream ois = new ObjectInputStream( gzin );
            Vector cats = (Vector) ois.readObject();
            ois.close();
            */

            HttpObjectReader reader = new HttpObjectReader( args[1] );

            ObjectStore os = ObjectStore.getInstance();
            os.setReader( reader );

            Vector cats = NewsManager.getInstance().newsCategories();
            
            NewsManager.writeNewsSources( cats, xmlFile );

            os.shutdown();
        }
        catch ( Exception ex )
        {
            ex.printStackTrace();
        }
    }
}